#include "game.h"

#include "gamestatemanager.h"

Game::Game(GameStateManager& gsm, GameInfo& gameInfo) :
    _gsm(gsm), _model(*new GameModel()), _gameInfo(gameInfo),
    _gui(*new GuiSystem()), _view(*new GameView(_model, _gui))
{
    _pieceSelected = false;
    _selPieceX = 0, _selPieceY = 0;
    _promotionDone = false, _delayedPromotion = false;
    _gameEnded = false;
}

Game::~Game()
{
    delete &_view;
    delete &_gui;
    delete &_model;
}

bool Game::update(float dt, const Input& input)
{
    if (_gameInfo.shouldQuit()) {
        reset();
        _gsm.pushState(GST_MENU);
        return false;
    }

    handleCameraInput(input);

    _view.updateAnimations(dt);

    // Show quit dialog if esc is pressed
    if (glfwGetKey(GLFW_KEY_ESC)) {
        _gsm.pushState(GST_CONFIRMQUIT);
        return true;
    }

    if (!_gameEnded) {
        TurnResult turnResult = TR_CONTINUE;
        if (_gameInfo.getPlayerTypeByColor(_model.getTurn()) == HUMAN) {

            turnResult = playHumanTurn(input);
            if (turnResult == TR_PROMOTION) {
                _promotionDone = true;
                _gsm.pushState(GST_PROMOTION);
            }
        } else {
            turnResult = playAiTurn();
        }

        if (turnResult == TR_WHITEWON) {
            _gameEnded = true;
            _gui.enable(GUI_WHITEWON);

        } else if (turnResult == TR_BLACKWON) {
            _gameEnded = true;
            _gui.enable(GUI_BLACKWON);
        }
    }


    return true;
}

TurnResult Game::playHumanTurn(const Input& input)
{
    TurnResult turnResult = TR_CONTINUE;

    if (_view.isReady()) {

        if (_delayedPromotion) {
            _delayedPromotion = false;
            return TR_PROMOTION;
        }

        if (_promotionDone) {
            // Promotion was selected, execute the actual move

            _promotionDone = false;

            const Move* m = _model.getPromotionMove(_gameInfo.getPromotion(),
                                                    _gameInfo.getPromotionX(),
                                                    _gameInfo.getPromotionY());

            MoveResult result = _model.makeMove(*m);
            _view.updateBoard();

            if (result == WHITE_WIN) {
                return TR_WHITEWON;
            } else if (result == BLACK_WIN) {
                return TR_BLACKWON;
            }

            return TR_CONTINUE;
        }

        /*
        GuiId button;
        if (_gui.interact(input, &button, (int*)NULL) == GE_CLICKED) {
            _gui.disable(button);
        }
        */

        int x, y;
        bool mouseOnSquare =
            _view.getClickedSquare(input.getCurrent().getMouseX(),
                                   input.getCurrent().getMouseY(), &x, &y);

        // Show/hide mouse hover marker
        if (mouseOnSquare) {
            _view.showMouseMarker(x, y);
        } else {
            _view.hideMouseMarker();
        }

        // LMB clicked on a square
        if (mouseOnSquare && !input.getPrevious().leftPressed() &&
                input.getCurrent().leftPressed()) {

            int color = _model.getTurn();
            int piece = _model.getPiece(x, y);

            // Clicked on own piece
            if (piece != EMPTY && _model.getPieceColor(piece) == color) {

                if(_pieceSelected && x ==_selPieceX && y == _selPieceY) {
                    _pieceSelected = false;
                    _view.clearHighlights();
                    _view.hideMouseMarker();
                } else {
                    _pieceSelected = true;
                    _selPieceX = x, _selPieceY = y;
                    _view.highlightMoves(x, y);
                }

            // Clicked on something else with own piece selected
            } else if (_pieceSelected) {

                int moveId;
                if (_model.isMoveValid(_selPieceX, _selPieceY, x, y,
                                      &moveId)) {
                    Move m = _model.getMove(moveId);

                    if (_model.isPromotionMove(m)) {
                        // Move is a promotion

                        _gameInfo.setPromotionX(x);
                        _gameInfo.setPromotionY(y);

                        _pieceSelected = false;
                        _view.clearHighlights();
                        _view.hideMouseMarker();

                        _view.animateMove(m);

                        _delayedPromotion = true;

                        return TR_CONTINUE;
                    }

                    _view.animateMove(m);

                    MoveResult result = _model.makeMove(m);

                    if (result == WHITE_WIN) {
                        turnResult = TR_WHITEWON;
                    } else if (result == BLACK_WIN) {
                        turnResult = TR_BLACKWON;
                    }

                }

                _pieceSelected = false;
                _view.clearHighlights();
                _view.hideMouseMarker();

            }
        }

    }

    return turnResult;
}

TurnResult Game::playAiTurn()
{
    TurnResult turnResult = TR_CONTINUE;

    if (_view.isReady()) {

        if (_model.isReady()) {

            _gui.enable(GUI_HOURGLASS);

            if (!_model.isAiThreadStarted()) {
                _model.startAiThread();
            } else {

                Move m = _model.getAiMove();
                _view.animateMove(m);
                MoveResult result = _model.makeMove(m);

                _gui.disable(GUI_HOURGLASS);

                if (result == WHITE_WIN) {
                    turnResult = TR_WHITEWON;
                } else if (result == BLACK_WIN) {
                    turnResult = TR_BLACKWON;
                }
            }
        }

    }

    return turnResult;
}

IGameStateView& Game::getView()
{
    return _view;
}

void Game::onEnter()
{
    _model.setAiDepth(_gameInfo.getAiDepth());
}

void Game::handleCameraInput(const Input& input)
{
    if (input.getCurrent().rightPressed() &&
        input.getPrevious().rightPressed()) {

        _view.getCamera().updateView(input.getDiff().getMouseX(),
                                     input.getDiff().getMouseY());
    }

    if (input.getDiff().getWheel() != 0) {
        _view.getCamera().updateZoom(-input.getDiff().getWheel());
    }
}

void Game::reset()
{
    _pieceSelected = false;
    _selPieceX = 0, _selPieceY = 0;
    _promotionDone = false, _delayedPromotion = false;
    _gameEnded = false;

    _gameInfo.reset();
    _model.reset();
    _view.reset();

}
